#include "Precompile.h"
#include "MaterialManager.h"

namespace Graphics{
	MaterialManager::MaterialManager():
		m_materialId(0),m_pixelShaderId(0),
		m_vertexShaderId(0),m_computeShaderId(0),
		m_activeMaterial(-1),m_activePixelShader(-1),
		m_activeVertexShader(-1),m_activeComputeShader(-1),
		m_inputLayoutId(0),m_activeInputLayout(-1),
		m_2DTextureId(0){
	}

	MaterialPtr MaterialManager::getMaterialByName(std::string &name){
		MaterialPtr ptr;
		std::map<std::string,unsigned int>::iterator itr = m_materialNameToIdMap.find(name);
		if(itr != m_materialNameToIdMap.end())
			ptr = m_materials[itr->second];
		return ptr;
	}

	MaterialPtr MaterialManager::getMaterial(unsigned int id){
		MaterialPtr ptr;
		std::map<unsigned int,MaterialPtr>::iterator itr = m_materials.find(id);
		if(itr != m_materials.end())
			ptr = itr->second;
		return ptr;
	}

	int MaterialManager::getVertexShaderIdByName(std::string &name){
		int result = -1;
		std::map<std::string,unsigned int>::iterator itr = m_vertexShaderNameMap.find(name);
		if(itr != m_vertexShaderNameMap.end()){
			result = itr->second;
		}

		return result;
	}

	int MaterialManager::getPixelShaderIdByName(std::string &name){
		int result = -1;
		std::map<std::string,unsigned int>::iterator itr = m_pixelShaderNameMap.find(name);
		if(itr != m_pixelShaderNameMap.end()){
			result = itr->second;
		}

		return result;
	}

	int	MaterialManager::getInputLayoutId(std::string &comparator){
		std::map<std::string,unsigned int>::iterator itr = m_inputLayoutTracker.find(comparator);
		if(itr == m_inputLayoutTracker.end()){
			return -1;
		}
		return itr->second;
	}
	
	unsigned int MaterialManager::addMaterial(MaterialPtr material){
		std::map<std::string,unsigned int>::iterator itr = m_materialNameToIdMap.find(material->m_name);
		if(itr != m_materialNameToIdMap.end()){
			LOG_DEBUG("Warning - Adding material with a name equal to already existing material using existing material.");
			return itr->second;
		}

		unsigned int id = m_materialId++;
		m_materialNameToIdMap[material->m_name] = id;
		m_materials[id] = material;
		return id;
	}

	void MaterialManager::removeMaterial(unsigned int id){
		std::map<unsigned int,MaterialPtr>::iterator itr = m_materials.find(id);
		if(itr == m_materials.end()){
			LOG_ERROR("Material does not exist");
			return;
		}

		m_materialNameToIdMap.erase(itr->second->m_name);
		for(unsigned int i=0; i<itr->second->m_textureIds.size(); i++){
			removeTexture2D(itr->second->m_textureIds[i]);
		}
		m_materials.erase(id);
	}

	unsigned int MaterialManager::addPixelShader(PixelShaderPtr shader){
		unsigned int id = m_pixelShaderId++;
		m_pixelShaderNameMap[shader->m_fileName] = id;
		m_pixelShaders[id] = shader;
		return id;
	}

	void MaterialManager::removePixelShader(unsigned int id){
		std::map<unsigned int,PixelShaderPtr>::iterator itr = m_pixelShaders.find(id);
		if(itr == m_pixelShaders.end()){
			LOG_ERROR("Trying to remove nonexistent pixel shader.");
			assert(0);
			return;
		}

		m_pixelShaderNameMap.erase(itr->second->m_fileName);
		m_pixelShaders.erase(id);
	}

	unsigned int MaterialManager::addVertexShader(VertexShaderPtr shader){
		unsigned int id = m_vertexShaderId++;
		m_vertexShaderNameMap[shader->m_fileName] = id;
		m_vertexShaders[id] = shader;
		return id;
	}

	void MaterialManager::removeVertexShader(unsigned int id){
		std::map<unsigned int,VertexShaderPtr>::iterator itr = m_vertexShaders.find(id);
		if(itr == m_vertexShaders.end()){
			LOG_ERROR("Trying to remove nonexistent vertex shader.");
			assert(0);
			return;
		}

		m_vertexShaderNameMap.erase(itr->second->m_fileName);
		m_vertexShaders.erase(id);
	}

	unsigned int MaterialManager::addComputeShader(ComputeShaderPtr shader){
		unsigned int id = m_computeShaderId++;
		m_computeShaders[id] = shader;
		return id;
	}

	void MaterialManager::removeComputeShader(unsigned int id){
		m_computeShaders.erase(id);
	}

	unsigned int MaterialManager::addInputLayout(VertexInputLayoutPtr layout){
		std::string comparator;
		for(unsigned int i=0; i<layout->m_sematics.size(); i++){
			comparator += layout->m_sematics[i];
		}

		std::map<std::string,unsigned int>::iterator compItr;
		if((compItr = m_inputLayoutTracker.find(comparator)) != m_inputLayoutTracker.end()){
			return compItr->second;
		}

		unsigned int id = m_inputLayoutId++;
		m_inputLayoutTracker[comparator] = id;
		m_inputLayouts[id] = layout;
		return id;
	}

	void MaterialManager::removeInputLayout(unsigned int id){
		std::map<unsigned int,VertexInputLayoutPtr>::iterator layItr = m_inputLayouts.find(id);
		if(layItr == m_inputLayouts.end()){
			LOG_ERROR("Trying to remove an nonexistent layout.");
			assert(0);
			return;
		}

		std::string comparator;
		for(unsigned int i=0; i<layItr->second->m_sematics.size(); i++){
			comparator += layItr->second->m_sematics[i];
		}

		m_inputLayoutTracker.erase(comparator);
		m_inputLayouts.erase(id);
	}

	unsigned int MaterialManager::addTexture2D(Texture2DPtr texture){
		unsigned int id = m_2DTextureId++;
		m_2DTextures[id] = texture;
		return id;
	}
	
	void MaterialManager::removeTexture2D(unsigned int id){
		m_2DTextures.erase(id);
	}

	void MaterialManager::setMaterial(unsigned int id){
		std::map<unsigned int, MaterialPtr>::iterator itr = m_materials.find(id);
		if(m_materials.end() == itr){
			LOG_ERROR("Material does not exist.");
			assert(0);
			return;
		}

		// Set textures
		MaterialPtr material = itr->second;
		for(unsigned int i=0; i<material->m_textureIds.size(); i++){
			m_2DTextures[material->m_textureIds[i]]->setTexture(m_renderContext);
		}

		if(material->m_vertexShaderId != -1)
			setVertexShader(material->m_vertexShaderId);
		if(material->m_pixelShaderId != -1)
			setPixelShader(material->m_pixelShaderId);
		if(material->m_computeShaderId != -1)
			setComputeShader(material->m_computeShaderId);
	}

	void MaterialManager::setPixelShader(unsigned int id){
		if(m_activePixelShader == id)
			return;

		m_pixelShaders[id]->setShader(m_renderContext);
		m_activePixelShader = id;
	}

	void MaterialManager::setVertexShader(unsigned int id){
		if(m_activeVertexShader == id)
			return;

		m_vertexShaders[id]->setShader(m_renderContext);
		m_activeVertexShader = id;
	}

	void MaterialManager::setComputeShader(unsigned int id){
		if(m_activeComputeShader == id)
			return;

		m_computeShaders[id]->setShader(m_renderContext);
		m_activeComputeShader = id;
	}
}